package logic;

import analisador.classesDeIdentificadores.Simbolo;
import analisador.classesDeIdentificadores.SimboloConstante;
import analisador.classesDeIdentificadores.SimboloFuncao;
import analisador.classesDeIdentificadores.SimboloProcedimento;
import analisador.classesDeIdentificadores.SimboloVariavel;
import java.util.Hashtable;
import java.util.List;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
//aa
public class Semantico implements Constants
{   
    SymbolTable st;
    
    //nome da última constante
    String ultimoConst;
    
    private String ultimaIdFuncao;
    
    /* Para ações 4, 5 e 6
        gravando ids temporariamente em array para poder setar os tipos dos símbolos depois que todos foram declarados.
        Ex: var x, y, z : inteiro;
        Gravar x, y e z no array temporário. Então, depois de declarar z e descobrir que o tipo dessas variáveis é pra ser interio, enquanto eu passo
        por todas essas variáveis do array temporário e vou setando o seu tipo
    */
    private String[] tempIdArray;
    //próxima posição livre do array temp de ids
    private int tempIdArrayNextEmptyPosition;
    
    public Semantico()
    {
        this.st = new SymbolTable();
        this.tempIdArray = new String[20];
        for( int i = 0; i < 20; i++ )
        {
            this.tempIdArray[i] = "";
        }
        this.tempIdArrayNextEmptyPosition = 0;
    }
    
    public void executeAction(int action, Token token) throws SemanticError
    {
        System.out.println("\u001B[34mAção #"+action+", Token: "+token);
        switch( action )
        {
            case 1 : 
                this.acaoUm( token );
                break;
            case 2 : 
                this.acaoDois(token );
                break;
            case 3 : 
                this.acaoTres(token );
                break;
            case 4 : 
                this.acaoQuatro(token );
                break;
            case 5 : 
                this.acaoCinco(token );
                break;
            case 6 : 
                this.acaoSeis(token );
                break;
            case 7 : 
                this.acaoSete(token );
                break;
            case 8 : 
                this.acaoOito(token );
                break;
            case 9 : 
                this.acaoNove(token );
                break;
            case 10 : 
                this.acaoDez(token );
                break;
            case 11 : 
                this.acaoOnze(token );
                break;
            case 12 : 
                this.acaoDoze(token );
                break;
            case 13 : 
                this.acaoTreze(token );
                break;
            case 14 : 
                this.acaoQuatorze(token );
                break;
            case 15 : 
                this.acaoQuinze(token );
                break;
            case 16 : 
                this.acaoDezesseis(token );
                break;
            case 17 : 
                this.acaoDezessete(token );
                break;
            case 18 : 
                this.acaoDezoito(token );
                break;
            case 19 : 
                this.acaoDezenove(token );
                break;
            case 20 : 
                this.acaoVinte(token );
                break;
            case 21 : 
                this.acaoVinteUm(token );
                break;
            case 22 : 
                this.acaoVinteDois(token );
                break;
            case 23 : 
                this.acaoVinteTres(token );
                break;
            case 24 : 
                this.acaoVinteQuatro(token );
                break;
            case 25 : 
                this.acaoVinteCinco(token );
                break;
            case 26 : 
                this.acaoVinteSeis(token );
                break;
            case 27 : 
                this.acaoVinteSete(token );
                break;
            case 28 : 
                this.acaoVinteOito(token );
                break;
            case 29 : 
                this.acaoVinteNove(token );
                break;
            case 30 : 
                this.acaoTrinta(token );
                break;
            case 31 : 
                this.acaoTrintaUm(token );
                break;
            case 32 : 
                this.acaoTrintaDois(token );
                break;
            case 33 : 
                this.acaoTrintaTres(token );
                break;
            case 34 : 
                this.acaoTrintaQuatro(token );
                break;
            case 35 : 
                this.acaoTrintaCinco(token );
                break;
            case 36 : 
                this.acaoTrintaSeis(token );
                break;
            case 37 : 
                this.acaoTrintaSete(token );
                break;
            case 38 : 
                this.acaoTrintaOito(token );
                break;
            case 39 : 
                this.acaoTrintaNove(token );
                break;
            case 40 : 
                this.acaoQuarenta(token );
                break;
            case 41 : 
                this.acaoQuarentaUm( token );
                break;
            case 44 : 
                this.acaoQuarentaQuatro( token );
                break;
            case 45 : 
                this.acaoQuarentaCinco( token );
                break;    
            case 52 : 
                this.acaoCinquentaDois( token );
                break;
            case 58:
                this.acaoCinquentaOito(token );
                break;
            case 67 : 
                this.acaoSessentaSete( token );
                break;
            case 68 : 
                this.acaoSessentaOito( token );
                break;
            case 69 : 
                this.acaoSessentaNove( token );
                break;
            case 70 : 
                this.acaoSetenta( token );
                break;
            case 71 : 
                this.acaoSetentaUm( token );
                break;
            case 72 : 
                this.acaoSetentaDois( token );
                break;
            case 73 : 
                this.acaoSetentaTres( token );
                break;
            case 74 : 
                this.acaoSetentaQuatro( token );
                break;
            case 75 : 
                this.acaoSetentaCinco( token );
                break;
            case 76 : 
                this.acaoSetentaSeis( token );
                break;
            case 77 : 
                this.acaoSetentaSete( token );
                break;
            case 78 : 
                this.acaoSetentaOito( token );
                break;
            case 79 : 
                this.acaoSetentaNove( token );
                break;
            case 80 :
                this.acaoOitenta( token );
                break;
            case 81 :
                this.acaoOitentaUm(token);
                break;
            case 82 :
                this.acaoOitentaDois(token);
                break;
            case 83 :
                this.acaoOitentaTres(token);
                break;
            case 84 :
                this.acaoOitentaQuatro(token);
                break;
            
        }
        
        
    }
    
    /**
     *  Efetua inicializações:
     *   Nível Atual (NA:=1)), Deslocamento (:=0),
     *   Variáveis
     *   de Contexto, etc...
     *   Insere id na Tabela de Símbolos (TS) juntamente com seus atributos (categoria = id
     *   programa e nível = NA
     * 
     * @param token
     * @throws SemanticError 
     */
    public void acaoUm( Token token ) throws SemanticError
    {   
        Hashtable<String, String> listaAtributos = new Hashtable<String, String>();
 
        Simbolo s = new Simbolo( token.getLexeme(), "id-programa" );
        s.setNivelDeclarado( 0 );
        s.setDeslocamento( 0 );
        this.st.addSymbol( s );
        
        //this.st.print();
    }
    
    /**
     * Se id já está declarado no NA, então ERRO(“Id já declarado”) senão insere id na TS, junto com seus atributos
     * (categoria = constante e nível = NA)
     * 
     * @param token
     * @throws SemanticError 
     */
    public void acaoDois( Token token ) throws SemanticError
    {
        //verificar se id já está declarado na tabela de símbolos no nível atual
        if( this.st.verificaIdJaDeclarado( token.getLexeme() ))
        {
            //já foi declarado
            System.out.println("&&&&&&&&&&&  ERRO VARIÁVEL JÁ DECLARADA  &&&&&&&&&&&&&&&");
            throw new SemanticError( "Identificador já foi declarado", token.getPosition() );
        }
        else
        {
            //inserir elemento na tabela de símbolos
            //categoria: id-constante
            SimboloConstante s = new SimboloConstante( token.getLexeme(), "id-constante" );
            s.setNivelDeclarado( this.st.getNivelAtual() );
            this.ultimoConst = token.getLexeme();
            this.st.addSymbol( s );
        }
        
        this.st.print();
    }
    
    public void acaoTres( Token token ) throws SemanticError
    {
        System.out.println( "&&&&&&&&&&&&&& ENTROU NA AÇÃO TRESSS &&&&&&&&&&&&&&&" );
        
        //nesse ponto da análise semântica já é sabido o tipo e o valor da constante
        this.st.setTipoConstanteParaId( this.ultimoConst );
        this.st.setValorConstanteParaId( this.ultimoConst );
        
        this.st.print();
    }
    
    public void acaoQuatro( Token token ) throws SemanticError
    {
        //seta contextoLID para decl 
        this.st.setContextoLID( "decl" );
        //Marca pos. do primeiro id da lista (relativa a TS)
        this.st.setaPrimeiroId( token.getLexeme() );
        
        this.limpeTempIdArray();
        
        this.st.print();
    }
    
    public void acaoCinco( Token token ) throws SemanticError
    {
        //#05 – Marca pos. do último id da lista (relativa a TS)
        this.st.setaUltimoId( token.getLexeme() );
        
        this.st.print();
    }
    
    public void acaoSeis( Token token ) throws SemanticError
    {
      //#06 – Preenche atributos na TS dos id’s da lista,
      //  considerando categoria “variável” e TipoAtual
        
        if( this.st.getTipoAtual().equalsIgnoreCase( "vetor" ) )
        {
            //se for vetor tem mais informações extras para guardas nos símbolos que foram declarados
            //passar por todos os ids que foram declarados e atualizar o seu tipo na tabela de símbolos
            for( String lexeme : this.tempIdArray )
            {
                if( lexeme.length() > 0 )
                {
                    
                    //buscar este id na ts e atualizar seu tipo( com o tipoAtual ) ( inteiro, real, etc )
                    //this.st.atualizaTipolId( lexeme );
                    
                    //atualizar tipo e valor dos intervalos inferior e superior
                    this.st.atualizaInfoVetor( lexeme );
                }
            }
            
            this.st.limpeDadosAuxVetor();
        }
        else
        {
            //passar por todos os ids que foram declarados e atualizar o seu tipo na tabela de símbolos
            for( String lexeme : this.tempIdArray )
            {
                if( lexeme.length() > 0 )
                {
                    //buscar este id na ts e atualizar seu tipo( com o tipoAtual ) ( inteiro, real, etc )
                    this.st.atualizaTipolId( lexeme );  
                }
            }
        }
        
        this.st.print();
        this.limpeTempIdArray();
    }
    
    public void acaoSete( Token token ) throws SemanticError
    {
        //verificar se o id do procedimento já está declarado no nível atual
        if( !this.st.verificaIdJaDeclarado( token.getLexeme() ) )
        {
             Simbolo s = new SimboloProcedimento( token.getLexeme() );
             s.setNivelDeclarado( this.st.getNivelAtual() );
             this.st.addSymbol( s );
             
             this.st.incrementaNivelAtual();
        }
        else
        {
            throw new SemanticError( "Erro ao declarar procedimento pois este identificador já foi utilizado", token.getPosition() );
        }
    }
    
    public void acaoOito( Token token ) throws SemanticError
    {
        //verificar se o id da função já está declarado no nível atual
        if( !this.st.verificaIdJaDeclarado( token.getLexeme() ) )
        {
             //guardar o nome da funcao para posteriormente poder atualizar o seu tipo de retorno
             this.ultimaIdFuncao = token.getLexeme();
            
             Simbolo s = new SimboloFuncao( token.getLexeme() );
             s.setNivelDeclarado( this.st.getNivelAtual() );
             this.st.addSymbol( s );
             
             this.st.incrementaNivelAtual();
        }
        else
        {
            throw new SemanticError( "Erro ao declarar a função pois este identificar já foi utilizado", token.getPosition() );
        }
    }
    
    public void acaoNove( Token token ) throws SemanticError
    {
        //já está sendo feita pela ação 14 quando o contexto for de par formais
    }
    
    public void acaoDez( Token token ) throws SemanticError
    {
        //atualizar o tipo de retorno da função que acabou de ser declarada
        this.st.atualizeTipoRetornoDaFuncao( this.ultimaIdFuncao );
    }
    
    public void acaoOnze( Token token ) throws SemanticError
    {
        //retirar as variáveis que foram declaradas nesse nível da tabela de símbolos
        //this.st.removaVariaveisDoNivelAtual();
        
        //atualiza nível atual(decrementar) pois saiu de um nível
        this.st.decrementaNivelAtual();
    }
    
    public void acaoDoze( Token token ) throws SemanticError
    {
        this.limpeTempIdArray();
        
        //setar contextoLID para par-formal
        this.st.setContextoLID( "par-formal" );
        
        this.st.setMpp(token.getLexeme());
    }
    
    public void acaoTreze( Token token ) throws SemanticError
    {
        //não há necessidade de implementar pois fizemos com outra lógica mais fácil
    }
    
    public void acaoQuatorze( Token token ) throws SemanticError
    {
        //ESTA PARTE FOI IMPLEMENTADA PELO GRUPO
        //EM UM CONTEXTO DE PAR-FORMAIS ONDE A PASSAGEM É POR REFERÊNCIA, VERIFICAR SE O TIPO DO PARÂMETRO PASSADO É DO MESMO TIPO DO ID JÁ DECLARADO EM
        //ALGUM NÍVEL ANTERIOR
        
        
        //atualizar todos os ids do array temporário de ids com a quantidade de parâmetros do procedimento, mpp(ref ou val) e tipoAtual
        for( String lexeme : this.tempIdArray )
        {
            if( lexeme.length() > 0 )
            {
                if( this.st.getMpp().equalsIgnoreCase( "ref" ) )
                {
                    if( !this.st.verificaIdDeclaradoEmNivelAnteriorComTipoAtual( lexeme ) )
                    {
                        //não encontrou o id com mesmo tipo declarado em algum nível anterior
                        throw new SemanticError( "Erro na passagem de parâmetro por referência pois é necessário que o identificador passado por parâmetro já tenha sido declarado em algum nível anterior e que estes sejam do mesmo tipo", token.getPosition() );
                    }
                }
            
                //atualiza o seu tipo ( inteiro, real, etc )
                this.st.atualizaTipolId( lexeme );
                
                //atualiza atributo mpp( ref ou val )
                this.st.atualizaMppId( lexeme );
            }
        }
    }
    
    public void acaoQuinze( Token token ) throws SemanticError
    {
        //seta mpp( método de passagem de parâmetro ) para referência
        this.st.setMpp( "ref" );
    }
    
    public void acaoDezesseis ( Token token ) throws SemanticError
    {
        //seta mpp( método de passagem de parâmetro ) para valor
        this.st.setMpp( "val" );
    }
    
    /*
     * #17 – Se contextoLID = “decl”
                entao se id já declarado no NA
                    então ERRO(“Id já declarado”)
                    senão insere id na TS
                    
            - Se contextoLID = “par-formal”
                entao se id já declarado no NA
                    então ERRO (“Identificador já declarado”)
                    senão incrementa NPF; insere id na TS
            - Se contextoLID = “leitura”
                entao se id não está declarado no NA
                    então ERRO (“Identificador não declarado”)
                    senão Se id é da categ. variável ou parâmetro
                        então se tipo <> pré-definido
                            entao ERRO(“tipo de id inválido”)
                            senão (* G Código para leitura *)
                        senao ERRO(“apenas var. podem ser lidas”)
     */
    
    /*
     * Trocar todas as posições do array temporário de ids para vazio ""
     */
    public void limpeTempIdArray()
    {
        //apontamento para a primeira posição do array
        this.tempIdArrayNextEmptyPosition = 0;
        
        int fim = this.tempIdArray.length;
        for( int i = 0; i < fim; i++ )
        {
            this.tempIdArray[i] = "";
        }
    }
    
    /**
     * Adicionar id ao array temporário de ids
     */
    public void addIdToTempIdArray( String lexeme )
    {
       this.tempIdArray[this.tempIdArrayNextEmptyPosition++] = lexeme;
       
       this.printTempIdArray();
    }
    
    /*
     * Imprimir no console as posições que estiverem armazenas no array temporário de ids
     */
    public void printTempIdArray()
    {
        System.out.println( "////////// IMPRIMINDO ARRAY TEMPORÁRIO DE IDS" );
        
        for( String lexeme : this.tempIdArray )
        {
            System.out.println( "Id: " + lexeme );;
        }
    }
    
    public void acaoDezessete ( Token token ) throws SemanticError
    {
        switch( this.st.getContextoLID() )
        {
            case "decl":
                //verificar se id já está declarado na tabela de símbolos no nível atual
                if( this.st.verificaIdJaDeclarado( token.getLexeme() ))
                {
                    //já foi declarado
                    System.out.println("&&&&&&&&&&&  ERRO VARIÁVEL JÁ DECLARADA  &&&&&&&&&&&&&&&");
                    throw new SemanticError( "Identificador já foi declarado", token.getPosition() );
                }
                else
                {
                    
                    Simbolo s = new SimboloVariavel( token.getLexeme(), "id-variavel" );
                    s.setNivelDeclarado( this.st.getNivelAtual() );
                    
                    //adicionar nome do id no array temporário de ids para depois poder setar os seus tipos
                    this.addIdToTempIdArray( token.getLexeme() );
                    
                    //pra isso aqui? está certo mesmo que é em ultimoConst que queres fazer a atribuição?
                    //this.ultimoConst = token.getLexeme();
                    this.st.addSymbol( s );
                }
                break;
            case "par-formal":
                //ação que o grupo inventou. SÓ PODE PASSAR UMA VARIÁVEL POR REFERÊNCIA SE ELA JÁ TIVER SIDO DECLARADA, OU SEJA, QUE ELA ESTEJA NA
                //TABELA DE SÍMBOLOS
                if( this.st.getMpp().equalsIgnoreCase( "ref" ) )
                {
                    //verificar se o id já foi declarado em algum nível anterior a este
                    if( !this.st.verificaIdJaDeclaradoEmNivelAnterior( token.getLexeme() ))
                    {
                        throw new SemanticError( "Para passar uma variável por referência é necessário que ela já tenha sido declarada", token.getPosition() );
                        
                    }
                }
                
                //verificar se id já está declarado na tabela de símbolos no nível atual
                if( this.st.verificaIdJaDeclarado( token.getLexeme() ))
                {
                    //já foi declarado
                    System.out.println("&&&&&&&&&&&  ERRO VARIÁVEL JÁ DECLARADA  &&&&&&&&&&&&&&&");
                    throw new SemanticError( "Identificador já foi declarado", token.getPosition() );
                }
                else
                {
                    //adicionar nome do id no array temporário de ids
                    this.addIdToTempIdArray( token.getLexeme() );
                    
                    //incrementar o número de par formais
                    this.st.incrementeNpf();
                }
                break;
            case "leitura":
                //verificar se id já está declarado na tabela de símbolos no nível atual
                if( !this.st.verificaIdJaDeclarado( token.getLexeme() ) )
                {
                    //já foi declarado
                    System.out.println("&&&&&&&&&&&  ERRO VARIÁVEL NÂO DECLARADA  &&&&&&&&&&&&&&&");
                    throw new SemanticError( "Identificador não foi declarado", token.getPosition() );
                }
                else
                {
                    //IMPLEMENTAR  
                    //verificar se id é da categoria variável ou parâmetro
                    //buscar símbolo
                    
                    /*
                    if(  )
                    {
                        
                    }
                    else
                    {
                       // System.out.println("&&&&&&&&&&&  Apenas variáveis podem ser lidas  &&&&&&&&&&&&&&&");
                        //throw new SemanticError( "Apenas variáveis podem ser lidas", token.getPosition() );
                    }
                    * */
                }
                break;    
        }
        this.st.print();
    }
    
    public void acaoDezoito ( Token token ) throws SemanticError
    {
        System.out.println( "TIPO DA CONSTANTE É: " + this.st.getTipoConstante());
        //verificar se o tipo da cadeia é inteiro
        if( this.st.getTipoConstante().equalsIgnoreCase( "inteiro" ) )
        {
            //verificar se o valor da constante para declaração do tamanho da cadeia é maio que 256
            if( this.st.getValorConstanteInteiro() > 256 )
            {
                throw new SemanticError( "Tamanho da cadeia é maior que o permitido", token.getPosition() );
            }
            else
            {
                //tudo certo na declaração de cadeia
                this.st.setTipoAtual( "cadeia" );
            }
        }
        else
        {
            throw new SemanticError( "Erro ao setar o tamanho da cadeia. Esperava-se uma constante inteira.", token.getPosition() );
        }
    }
    
    public void acaoDezenove ( Token token ) throws SemanticError
    {
        //análise para a parte inferior do vetor
        
        //verificar se tipo da constante é diferente de inteiro e caracter
        if( !this.st.getTipoConstante().equalsIgnoreCase( "inteiro" ) && !this.st.getTipoConstante().equalsIgnoreCase( "caracter" ) )
        {
            throw new SemanticError( "Tipo de índice inválido", token.getPosition() );
        }
        else
        {
            if( this.st.getTipoConstante().equalsIgnoreCase( "inteiro" ) )
            {
                //verificar se está sendo declarado a primeira ou a segunda dimensão
                if( this.st.getNumeroDimensoesVetor() == 2 )
                {
                    //dimisão um já foi declarada. Então agora é para declarar a dimensão 2
                    this.st.setTipoLimiteInferiorDim2( this.st.getTipoConstante() );
                    this.st.setValorInteiroInferiorDim2( Integer.parseInt( token.getLexeme() ) );

                }
                else
                {
                    //guardar tipo e valor do limite inferior do vetor da dimensão 1
                    this.st.setTipoLimiteInferiorDim1( this.st.getTipoConstante() );
                    this.st.setValorInteiroInferiorDim1( Integer.parseInt( token.getLexeme() ) );
                }
            }
            else
            {
                //intervalo do vetor é caracter
                //verificar se está sendo declarado a primeira ou a segunda dimensão
                if( this.st.getTipoLimiteInferiorDim1().length() > 0 )
                {
                    //dimisão um já foi declarada. Então agora é para declarar a dimensão 2
                    this.st.setTipoLimiteInferiorDim2( this.st.getTipoConstante() );
                    this.st.setValorCaracterInferiorDim2( token.getLexeme() );

                }
                else
                {
                    //guardar tipo e valor do limite inferior do vetor da dimensão 1
                    this.st.setTipoLimiteInferiorDim1( this.st.getTipoConstante() );
                    this.st.setValorCaracterInferiorDim1( token.getLexeme() );
                }    
            }
        }
    }
    
    public void acaoVinte ( Token token ) throws SemanticError
    {
        //análise para a parte superior do vetor
        
        //verificar se constantes de nível inferior e superior são compatíveis
        if( this.st.getTipoConstante().equalsIgnoreCase( this.st.getTipoLimiteInferiorDim1() ) )
        {
            //verificar se o valor declarado no limite superior do intervalo é menor que o inferior
            if( Integer.parseInt( token.getLexeme() ) <= this.st.getValorInteiroInferiorDim1()  )
            {
                throw new SemanticError( "O limite superior deve ser maior que o limite inferior do vetor", token.getPosition() );
            }
            else
            {
                if( this.st.getTipoConstante().equalsIgnoreCase( "inteiro" ) )
                {
                    //guardar tipo e valor do limite superior do vetor
                    //verificar se esta é a primeira ou a segunda dimensão a ser declarada
                    if( this.st.getNumeroDimensoesVetor() == 2 )
                    {
                        //esta é a segunda dimensão do vetor
                        this.st.setTipoLimiteSuperiorDim2( this.st.getTipoConstante() );
                        this.st.setValorInteiroSuperiorDim2( Integer.parseInt( token.getLexeme() ) );
                    }
                    else
                    {
                        //declarando a parte superior da primeira dimensão
                        //guardar tipo e valor do limite inferior do vetor da dimensão 1
                        this.st.setTipoLimiteSuperiorDim1( this.st.getTipoConstante() );
                        this.st.setValorInteiroSuperiorDim1( Integer.parseInt( token.getLexeme() ) );
                    }
                }
                else
                {
                    //tipo dos valores é caracter
                    //guardar tipo e valor do limite superior do vetor
                    //verificar se esta é a primeira ou a segunda dimensão a ser declarada
                    if( this.st.getNumeroDimensoesVetor() == 2 )
                    {
                        //esta é a segunda dimensão do vetor
                        this.st.setTipoLimiteSuperiorDim2( this.st.getTipoConstante() );
                        this.st.setValorCaracterSuperiorDim2( token.getLexeme() );
                    }
                    else
                    {
                        //declarando a parte superior da primeira dimensão
                        //guardar tipo e valor do limite inferior do vetor da dimensão 1
                        this.st.setTipoLimiteSuperiorDim1( this.st.getTipoConstante() );
                        this.st.setValorCaracterSuperiorDim1( token.getLexeme() );
                    }
                } 
            }
        }
        else
        {
            throw new SemanticError( "As constantes do intervalo do vetor devem ser do mesmo tipo", token.getPosition() );
        }
    }
    
    public void acaoVinteUm( Token token ) throws SemanticError
    {
        //guardar tipo dos elementos do vetor, tipoAtual, e número de dimensoes
        this.st.setTipoAtual( "vetor" );
        this.st.setTipoElementosVetor( token.getLexeme() );
        this.st.print();
    }
    
    public void acaoVinteDois( Token token ) throws SemanticError
    {
        //seta número de dimensões para dois
        this.st.setNumeroDimensoesVetor( 2 );
    }
    
    public void acaoVinteTres( Token token ) throws SemanticError
    {
       //seta número de dimensões para um
       this.st.setNumeroDimensoesVetor( 1 );
    }
    
    public void acaoVinteQuatro( Token token ) throws SemanticError
    {
        //verifica se o id já está declarado
        if( this.st.verificaIdJaDeclarado( token.getLexeme() ) )
        {
            //verifica se a categoria da id é uma constante
            if( this.st.comparaCategoriaId( token.getLexeme(), "id-constante" ) )
            {
                //buscar o simbolo na tabela de símbolos para atualizar tipoConst e valConst
                Simbolo s = this.st.getSimbolo( token.getLexeme() );
                this.st.setTipoConstante(s.getTipoConstante() );
                
                if( s.getTipoConstante().equalsIgnoreCase( "inteiro" ) )
                {
                    this.st.setValorConstanteInteiro( s.getValorConstanteInteiro() );
                }
                else if( s.getTipoConstante().equalsIgnoreCase( "real" ) )
                {
                    this.st.setValorConstanteReal( s.getValorConstanteReal() );
                }
                else if( s.getTipoConstante().equalsIgnoreCase( "literal" ) )
                {
                    this.st.setValorConstanteLiteral( s.getValorConstanteLiteral() );
                }
                else if( s.getTipoConstante().equalsIgnoreCase( "booleano" ) )
                {
                    this.st.setValorConstanteBoolean( s.isValorConstanteBoolean() );
                }
            }
            else
            {
                throw new SemanticError( "Esperava-se que a categoria da id fosse id-constante", token.getPosition() );
            }
        }
        else
        {
            //id já está declarado. Erro!
            throw new SemanticError( "Erro. Id não foi declarado!", token.getPosition() );
        }
    }
    
    public void acaoVinteCinco( Token token ) throws SemanticError
    {
        this.st.setTipoAtual("inteiro");
    }
    
    public void acaoVinteSeis( Token token ) throws SemanticError
    {
        this.st.setTipoAtual("real");
    }
    
    public void acaoVinteSete( Token token ) throws SemanticError
    {
        this.st.setTipoAtual("booleano");
    }
    
    public void acaoVinteOito( Token token ) throws SemanticError
    {
        this.st.setTipoAtual("caracter");
    }
     /**
      *   #29 – Se id não está declarado (não esta na TS)
      *  então ERRO(“Identificador não declarado”)
      *  senão guarda posição de id (POSID)
      */
    public void acaoVinteNove( Token token ) throws SemanticError
    {
        /*
         #29 – Se id não está declarado (não esta na TS)
        então ERRO(“Identificador não declarado”)
        senão guarda posição de id (POSID)
         */
        if( !this.st.verificaIdJaDeclarado( token.getLexeme() ) )
        {
            
            System.out.println("&&&&&&&&&&&  ERRO VARIÁVEL NÂO DECLARADA  &&&&&&&&&&&&&&&");
            throw new SemanticError( "Identificador não foi declarado", token.getPosition() );
        }
        else
        {
            this.st.setIdComando( token.getLexeme() );   
        }
    }
    /**
     * #30 – Se TipoExpr <> “booleano” e <> “inteiro”
     * então ERRO(“Tipo inválido da expressão”)
     * senao (* G. Código *)
     * @param token
     * @throws SemanticError 
     */
    public void acaoTrinta( Token token ) throws SemanticError
    {
        String tipoExpr = this.st.getTipoExp();
        
        if( !tipoExpr.equals( "booleano" ) && !tipoExpr.equals( "inteiro" ) )
        {
            throw new SemanticError( "Tipo da expressão deve ser booleano ou inteiro", token.getPosition() );
        }
        else
        {
           //senao (* G. Código *) 
        }
        
    }
    
    public void acaoTrintaUm( Token token ) throws SemanticError
    {
        //seta contextoLID para leitura
        this.st.setContextoLID("leitura");
    }
    
    public void acaoTrintaDois( Token token ) throws SemanticError
    {
      /*
       #32 – Seta contextoEXPR para “impressão”
        se TipoExpr <> inteiro/real/caracter/cadeia
        então ERRO(“tipo invalido para impressão”)
        senão (* G. Código *)
       */
        this.st.setContextoEXPR( "impressao" );
        String tipoExpr = this.st.getTipoExp();
        if( !tipoExpr.equals( "inteiro" ) && !tipoExpr.equals( "real" ) && !tipoExpr.equals( "caracter" ) && !tipoExpr.equals( "cadeia" ))
        {
            throw new SemanticError( "tipo invalido para impressão: deve ser inteiro/real/caracter/cadeia", token.getPosition() );
        }
        else
        {
           //senao (* G. Código *) 
        }
        
    }
    
    public void acaoTrintaTres( Token token ) throws SemanticError
    {
        /*
         * #33 – Se categoria de id = “Variável” ou “Parâmetro”
            então se tipo de id = “vetor”
            então ERRO (“id. Deveria ser indexado”)
            senão TipoLadoEsq := tipo de id
            senão se categoria de id = “função”
            então se fora do escopo da função id
            então ERRO(“fora do escopo da função”)
            senão TipoLadoEsq := tipo da função
            senão ERRO (“id. deveria ser var/par/função”)
         */
        Simbolo aux  = this.st.getSimbolo(token.getLexeme());
        String tipoCat = aux.getCategoria();
        switch (tipoCat) {
            case "id-variavel":
            case "id-parametro":
                if( aux.getTipoVariavel().equals( "vetor" ) )
                {
                    throw new SemanticError( "id. Deveria ser indexado", token.getPosition() );
                }
                else
                {
                    this.st.setTipoLadoEsquerdo( aux.getTipoVariavel() ); 
                }
                break;
            case "id-funcao":
                /*
                 *  então se fora do escopo da função id
                    então ERRO(“fora do escopo da função”)
                    senão TipoLadoEsq := tipo da função
                 */
                if( true ) //this.st.getTipoExp().equals("funcao") )
                {
                    throw new SemanticError( "fora do escopo da função", token.getPosition() ); 
                }
                else
                {
                    this.st.setTipoLadoEsquerdo(this.st.getTipoExp());
                }
                break;
            default:
                throw new SemanticError( "id deveria ser var/par/funcao", token.getPosition() );
        }
    }
    
    public void acaoTrintaQuatro( Token token ) throws SemanticError
    { 
        /*
         * se TipoExpr não compatível com tipoLadoesq
            então ERRO (“tipos incompatíveis”)
            senão (* G. Código *)
         */
        System.out.println( "¨%%%%%%%%%%%%%%%%%%%% this.st.getTipoLadoEsquerdo(): " + this.st.getTipoLadoEsquerdo() );
        System.out.println( "¨%%%%%%%%%%%%%%%%%%%% this.st.getTipoExp(): " + this.st.getTipoExp() );
        
        
        if( !this.st.getTipoExp().equals( this.st.getTipoLadoEsquerdo() ) )
        {
            throw new SemanticError( "tipos incompatíveis", token.getPosition() );
        }
       // else
       // {
       //    //(* G. Código *)     
        //} 
    
    }
       
    public void acaoTrintaCinco( Token token ) throws SemanticError
    {
        /*
         * #35 – se categoria de id <> “variável”
                então ERRO (“esperava-se uma variável”)
                senao se tipo de id <> vetor e <> de cadeia
                6
                então ERRO(“apenas vetores e cadeias podem
                ser indexados”)
                senão TipoVarIndexada = tipo (vetor/cadeia)
         */
        Simbolo sim = this.st.getSimbolo(token.getLexeme());
        String cat = sim.getCategoria();
        if( !cat.equals( "id-variavel" ) )
        {
            throw new SemanticError( "esperava-se uma variável", token.getPosition() );
        }
        else if( !sim.getTipoVariavel().equals("vetor") && !sim.getTipoVariavel().equals("cadeia") )
        {
           throw new SemanticError( "apenas vetores e cadeias podem ser indexados", token.getPosition() ); 
        }
        else
        {
            this.st.setTipoVarIndexada(sim.getTipoVariavel());
        }
    }
    
    public void acaoTrintaSeis( Token token ) throws SemanticError
    {
        /*
         Seta número de índices para 1
se TipoVarIndexada = vetor
então se TipoExpr <> TipoIndice da dimensão 1
então ERRO(“tipo do índice inválido”)
senão TipoLadoEsq := TipoElementos (do vetor)
senao (* é cadeia *)
se TipoExpr <> “inteiro”
então ERRO(“índice deveria ser inteiro”)
senão TipoLadoEsq := caracter
         */
        this.st.setNumeroDeIndices(1);
        if( this.st.getTipoVarIndexada().equals("vetor") )
        {
            if( !this.st.getTipoExp().equals(this.st.getTipoIndiceDimensao1()))
            {
               throw new SemanticError( "tipo do índice inválido", token.getPosition() ); 
            }
            else
            {
                this.st.setTipoLadoEsquerdo(this.st.getTipoElementosVetor());
            }
        }
        else //(* é cadeia *)
        {
            if( !this.st.getTipoExp().equals("inteiro") )
            {
                throw new SemanticError( "índice deveria ser inteiro", token.getPosition() ); 
            }
            else
            {
                this.st.setTipoLadoEsquerdo("caracter");
            }
        }
    }
    
    public void acaoTrintaSete( Token token ) throws SemanticError
    {
        /*
         se número de índices = 2
Então se TipoVarIndexada = cadeia
então ERRO(“Cadeia só pode ter 1 índice”)
senão se num-dimensoes do vetor <> 2
então ERRO (“Vetor é uni-dimensional”)
senão se TipoExpr <> TipoIndice da dim 2
então ERRO(“tipo índice inválido”)
senão TipoLadoEsq := TipoElementos
senão se número de dimensões = 2
então ERRO(“Vetor é bi-dimensional”)
         */
        if( this.st.getNumeroDeIndices() == 2 )
        {
            if( this.st.getTipoVarIndexada().equals("cadeia"))
            {
               throw new SemanticError( "Cadeia só pode ter 1 índice", token.getPosition() ); 
            }
            else 
            {
                if( this.st.getNumeroDimensoesVetor() != 2 )
                {
                    throw new SemanticError( "Vetor é uni-dimensional", token.getPosition() ); 
                }
                else if( !this.st.getTipoExp().equals( this.st.getTipoIndiceDimensao2() ))
                {
                    throw new SemanticError( "Tipo de índice inválido", token.getPosition() );
                }
                else
                {
                    this.st.setTipoLadoEsquerdo(this.st.getTipoElementosVetor());
                }
            }
        }
        else if( this.st.getNumeroDimensoesVetor() == 2)
        {
            throw new SemanticError( "Vetor é bi-dimensional", token.getPosition() );
        }
    }
    
    public void acaoTrintaOito( Token token ) throws SemanticError
    {
        //#38 – se categoria de id <> procedure
        //então ERRO(“id deveria ser uma procedure”)
        Simbolo sim = this.st.getSimbolo(token.getLexeme());
        String cat = sim.getCategoria();
        if( !cat.equals("id-procedure") )
        {
            throw new SemanticError( "id deveria ser uma procedure", token.getPosition() ); 
        }
    }
    
    public void acaoTrintaNove( Token token ) throws SemanticError
    {
        /*
         * NPA := 1 (Número de Parâmetros Atuais)
            seta contextoEXPR para “par-atual”
            Verifica se existe Parâmetro Formal correspondente e
            se o tipo e o MPP são compatíveis
         */
        this.st.setNpa(1);
        this.st.setContextoEXPR("par-atual");
        //Verifica se existe Parâmetro Formal correspondente
        //se o tipo e o MPP são compatíveis
    }
    
    public void acaoQuarenta( Token token ) throws SemanticError
    {
        /*
         * se NPA = NPF
            então (* G. Código para chamada de proc*)
            senão ERRO(“Erro na quantidade de parâmetros”)
         */
        if( this.st.getNpa() == this.st.getNpf())
        {
            //então (* G. Código para chamada de proc*)
        }
        else
        {
            throw new SemanticError( "Erro na quantidade de parâmetros", token.getPosition() );
        }
    }
    
    public void acaoQuarentaUm( Token token ) throws SemanticError
    {
        /*
         * se categoria de id <> procedure
            então ERRO(“id deveria ser uma procedure”)
            senão se NPF <> 0
            então ERRO(“Erro na quant.de parâmetros”)
            senão (* G. Código p/ chamada de proc. *)
         */
        Simbolo sim = this.st.getSimbolo(token.getLexeme());
        String cat = sim.getCategoria();
        if( !cat.equals("id-procedure") )
        {
            throw new SemanticError( "id deveria ser uma procedure", token.getPosition() );
        }
        else if( this.st.getNpf() != 0 )
        {
            throw new SemanticError( "Erro na quant.de parâmetros", token.getPosition() );
        }
        else
        {
           //(* G. Código p/ chamada de proc. *) 
        }
    }
    public void acaoQuarentaDois( Token token) throws SemanticError
    {
        //#42 – Seta numero de índices para 2
        this.st.setNumeroDeIndices(2);
    }
    public void acaoQuarentaTres( Token token ) throws SemanticError
    {
        /*
        se ContextoEXPR = “par-atual”
        então incrementa NPA e Verifica se existe Parâmetro
        Formal correspondente e se o tipo e o MPP
        são compatíveis
        - se ContextoEXPR = “impressão”
        entao se TipoExpr <> inteiro/real/caracter/cadeia
        então ERRO(“tipo invalido para impressão”)
        senão (* G. Código para impressão *)
         */
        switch (this.st.getContextoEXPR()) {
            case "par-atual":
                this.st.setNpa(this.st.getNpa() + 1);
                //this.st.getMpp()
                break;
            case "impressao":
                if( !this.st.getTipoExp().equals("inteiro") && !this.st.getTipoExp().equals("real") && !this.st.getTipoExp().equals("cadeia") && !this.st.getTipoExp().equals("caracter"))
                {
                    throw new SemanticError( "tipo invalido para impressão", token.getPosition() );
                }
                else
                {
                    //(* G. Código para impressão *)
                }
                break;
        }
    }
    
    
    
    public void acaoQuarentaQuatro( Token token ) throws SemanticError
    {
        //#44 – TipoExpr := TipoExpSimples
        
        System.out.println( "&&&&&&&&&&&&&&&&&&&&& this.st.getTipoExpSimples(): " + this.st.getTipoExpSimples() );
        this.st.setTipoExp(this.st.getTipoExpSimples());
    }
    
    public void acaoQuarentaCinco( Token token ) throws SemanticError
    {
        //#45 – Se TipoExpSimples incompatível com TipoExpr
        //então ERRO (“Operandos incompatíveis”)
        //senão TipoExpr := “booleano”
        if( !this.st.getTipoExpSimples().equals(this.st.getTipoExp()))
        {
            throw new SemanticError( "Operandos incompatíveis", token.getPosition() );
        }
        else
        {
            this.st.setTipoExp("booleano");
        }
        
    }
    
    public void acaoCinquentaOito( Token token ) throws SemanticError
    {
        //#58 – TipoTermo := TipoFator”
        System.out.println( "!!!!!!!!!!!!!!!!!! this.st.getTipoFator(): " + this.st.getTipoFator() );
        
        this.st.setTipoTermo(this.st.getTipoFator());
    }
    public void acaoSetentaUm( Token token ) throws SemanticError
    {
        this.st.setOpNega( "false" );
        this.st.setOpUnario( "false" );
    }
    
    public void acaoSetentaDois( Token token ) throws SemanticError
    {
       this.st.setTipoFator( this.st.getTipoExp() );
    }
    
    private void acaoOitenta(Token token) {
        this.st.setTipoConstante("inteiro");
        this.st.setValorConstanteInteiro( Integer.parseInt( token.getLexeme() ) );
        
        this.st.print();
    }
    
    private void acaoOitentaUm(Token token) {
        //guardar tipo e valor da constante para num-int
        this.st.setTipoConstante("real");
        this.st.setValorConstanteReal( Double.parseDouble( token.getLexeme() ) );
        
        this.st.print();
    }

    private void acaoOitentaDois(Token token) {
        this.st.setTipoConstante("falso");
        this.st.setValorConstanteBoolean( Boolean.parseBoolean( token.getLexeme() ));
        
        this.st.print();
    }
    
    private void acaoOitentaTres(Token token) {
        this.st.setTipoConstante("verdadeiro");
        this.st.setValorConstanteBoolean( Boolean.parseBoolean( token.getLexeme() ));
        
        this.st.print();
    }
    
     private void acaoOitentaQuatro(Token token) {
        this.st.setTipoConstante("literal");
        this.st.setValorConstanteLiteral( token.getLexeme() );
        
        this.st.print();
    }

    private void acaoSetentaTres(Token token) {
        System.out.println("$$$$$$$$$$$$ this.st.getTipoVar(): " + this.st.getTipoVar());
        
        this.st.setTipoFator( this.st.getTipoVar() );
    }

    private void acaoSetentaQuatro(Token token) {
        System.out.println( "&&&&&&&&&&&&&&&&&&&&& this.st.getTipoConstante(): " + this.st.getTipoConstante() );
        
        this.st.setTipoFator( this.st.getTipoConstante());
    }

    private void acaoSetentaCinco(Token token) throws SemanticError  {
        //#75 – se categoria de id <> função
        //então ERRO(“id deveria ser uma função”)
        Simbolo sim = this.st.getSimbolo(token.getLexeme());
        String cat = sim.getCategoria();
        if( !cat.equals("id-funcao") )
        {
            throw new SemanticError( "id deveria ser uma função", token.getPosition() );
        }
    }

    private void acaoSetentaSeis(Token token) throws SemanticError {
       
        // se NPA = NPF
        //então TipoVar := Tipo do resultado da função
        //(* G. Código chamada de função *)
        //senão ERRO(“Erro na quantidade de parâmetros”)
        if( this.st.getNpa() == this.st.getNpf() )
        {
            
           //então TipoVar := Tipo do resultado da função
            //(* G. Código chamada de função *)
        }
        else
        {
            throw new SemanticError( "Erro na quantidade de parâmetros", token.getPosition() ); 
        }
    }

    private void acaoSetentaSete(Token token) throws SemanticError {
        
        /*
         Seta número de índices para 1
            se TipoVarIndexada = vetor
            então se TipoExpr <> TipoIndice da dimensão 1
            então ERRO(“tipo do índice inválido”)
            senão TipoVar := TipoElementos do vetor
            senao (* é cadeia *)
            se TipoExpr <> “inteiro”
            então ERRO(“índice deveria ser inteiro”)
            senão TipoVar := caracter
         */
        this.st.setNumeroDeIndices(1);
        if( this.st.getTipoVarIndexada().equals("vetor") )
        {
            if( !this.st.getTipoExp().equals(this.st.getTipoIndiceDimensao1()))
            {
               throw new SemanticError( "tipo do índice inválido", token.getPosition() ); 
            
            //
            }//
            
            else
            {
                this.st.setTipoVar(this.st.getTipoElementosVetor());
            }//
        }//
        else //(* é cadeia *)
        {
            if( !this.st.getTipoExp().equals("inteiro") )
            {
                throw new SemanticError( "índice deveria ser inteiro", token.getPosition() ); 
            }//
            else
            {
                this.st.setTipoVar("caracter");
            }//
        }//
    
    }

    private void acaoSetentaOito(Token token) throws SemanticError {
        //th
        if( this.st.getNumeroDeIndices() == 2 )
        {
            if( this.st.getTipoVarIndexada().equals("cadeia") )
            {
                throw new SemanticError( "Cadeia só pode ter 1 índice", token.getPosition() ); 
            }else if( this.st.getNumeroDimensoesVetor() != 2)
            {
                throw new SemanticError( "vetor é unidimensional", token.getPosition() ); 
            }else if( !this.st.getTipoExp().equals(this.st.getTipoIndiceDimensao2()))
            {
                throw new SemanticError( "tipo índice inválido", token.getPosition() );
            }else
            {
               this.st.setTipoVar(this.st.getTipoElementosVetor());       }
        }else{
            if( this.st.getNumeroDimensoesVetor() == 2 )
                throw new SemanticError( "vetor é bidimensional", token.getPosition() );}
    }
    //doc
    private void acaoSetentaNove(Token token) throws SemanticError {
        /*
         * #33 – Se categoria de id = “Variável” ou “Parâmetro”
            então se tipo de id = “vetor”
            então ERRO (“id. Deveria ser indexado”)
            senão TipoLadoEsq := tipo de id
            senão se categoria de id = “função”
            então se fora do escopo da função id
            então ERRO(“fora do escopo da função”)
            senão TipoLadoEsq := tipo da função
            senão ERRO (“id. deveria ser var/par/função”)
         */
        Simbolo aux  = this.st.getSimbolo(token.getLexeme());
        String tipoCat = aux.getCategoria();
        switch (tipoCat) {
            case "id-variavel":
            case "id-parametro":
                if( aux.getTipoVariavel().equals( "vetor" ) )
                {
                    throw new SemanticError( "id. Deveria ser indexado", token.getPosition() );
                }
                else
                {
                    System.out.println( "*((((((((((((((( aux.getTipoVariavel(): " + aux.getTipoVariavel() );
                    this.st.setTipoLadoEsquerdo( aux.getTipoVariavel() );
                }
                break;
            case "id-funcao":
                if( this.st.getNpf() != 0 )
                {
                    throw new SemanticError( "Erro na quantidade de parâmetros", token.getPosition() );
                }
                else
                {
                    this.st.setTipoVar( this.st.getTipoExp() );
                }
                break;
            case "id-constante":
                this.st.setTipoVar( aux.tipoConstante() );
                break;
            default:
                throw new SemanticError( "id deveria ser var/par/funcao", token.getPosition() );
        }
    }

    private void acaoCinquentaDois(Token token) {
        
        System.out.println( "################### this.st.getTipoTermo(): " + this.st.getTipoTermo() );
        this.st.setTipoExpSimples( this.st.getTipoTermo() );
    }

    private void acaoSessentaSete(Token token) throws SemanticError {
        if( this.st.getOpNega().equals("true") )
        {
            throw new SemanticError( "Operadores não consecutivos", token.getPosition() );
        }
        else
        {
            this.st.setOpNega("true");
        }
    }

    private void acaoSessentaOito(Token token) throws SemanticError {
        if( !this.st.getTipoFator().equalsIgnoreCase( "booleano" ) )
        {
            throw new SemanticError( "Operador não exige booleano", token.getPosition() );
        }
    }

    private void acaoSessentaNove(Token token) throws SemanticError {
        if( this.st.getOpUnario().equalsIgnoreCase("true") )
        {
            throw new SemanticError( "Erro operadores unários consecutivos", token.getPosition() );
        }
        else
        {
            this.st.setOpUnario("true");
        }
    }

    private void acaoSetenta(Token token) throws SemanticError {
        if( !this.st.getTipoFator().equalsIgnoreCase("inteiro") && !this.st.getTipoFator().equalsIgnoreCase("real"))
        {
            throw new SemanticError( "Operadores + e - exigem operando numérico", token.getPosition() );
        }
    }
    
    
}
